﻿using MQTTnet;
using MQTTnet.Client;
using MQTTnet.Protocol;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace WinformMqttClient
{
    public class MqttClientService
    {
        public static IMqttClient _mqttClient;
        private Form1 _form1;
        private string mqttServer = "127.0.0.1";
        private int mqttPort = 10086;
        private string mqttUser = "admin";
        private string mqttPassword = "123456";
        private string mqttClientId = "testclient02";
        private string subTopic = "testclient02";
        private string pubTopic = "pubTopic";
        private CancellationTokenSource _cancellationTokenSource;
        private CancellationToken _cancellationToken;
        public MqttClientService(Form1 form1)
        {
            _form1 = form1;
            _cancellationTokenSource = new CancellationTokenSource();
            _cancellationToken = _cancellationTokenSource.Token;
        }
        public void MqttClientStart()
        {
            mqttServer = _form1.MqttServerIp;
            mqttPort = _form1.MqttPort;
            mqttUser = _form1.UserName;
            mqttPassword = _form1.Password;
            mqttClientId = _form1.ClientId;
            subTopic = _form1.MqttSubTopic;
            pubTopic = _form1.MqttPubTopic;

            var optionsBuilder = new MqttClientOptionsBuilder();
            optionsBuilder.WithTcpServer(mqttServer, mqttPort); // 要访问的mqtt服务端的 ip 和 端口号
            optionsBuilder.WithCredentials(mqttUser, mqttPassword); // 要访问的mqtt服务端的用户名和密码
            optionsBuilder.WithClientId(mqttClientId); // 设置客户端id
            optionsBuilder.WithCleanSession();
            optionsBuilder.WithTls(new MqttClientOptionsBuilderTlsParameters{UseTls = false  });// 是否使用 tls加密

            var clientOptions = optionsBuilder.Build();
            _mqttClient = new MqttFactory().CreateMqttClient();

            _mqttClient.ConnectedAsync += _mqttClient_ConnectedAsync; // 客户端连接成功事件
            _mqttClient.DisconnectedAsync += _mqttClient_DisconnectedAsync; // 客户端连接关闭事件
            _mqttClient.ApplicationMessageReceivedAsync += _mqttClient_ApplicationMessageReceivedAsync; // 收到消息事件
            try
            {
                //_mqttClient.TryPingAsync(); // 心跳检测
                _mqttClient.ConnectAsync(clientOptions);
            }catch(Exception ex)
            {
                Console.WriteLine("ConnectAsync :" + ex.StackTrace);
            }
        }
        public void Disconnect()
        {
            _mqttClient.DisconnectAsync().ContinueWith(new Action<Task>((task) => {
                _cancellationTokenSource.Cancel();
            }));
        }
        private static async Task ConnectAndPublishWithHeartBeat(string clientId)
        {
            try
            {
                //var optionsBuilder = new MqttClientOptionsBuilder();
                //optionsBuilder.WithTcpServer("127.0.0.1", 10086); // 要访问的mqtt服务端的 ip 和 端口号
                //optionsBuilder.WithCredentials("admin", "123456"); // 要访问的mqtt服务端的用户名和密码
                //optionsBuilder.WithClientId("testclient02"); // 设置客户端id
                //optionsBuilder.WithCleanSession();
                //optionsBuilder.WithTls(new MqttClientOptionsBuilderTlsParameters { UseTls = false });// 是否使用 tls加密

                //var clientOptions = optionsBuilder.Build();
                //await Task.Delay(1000);

               // while (true)
                {
                    await Task.Delay(3000);
                    var heartbeatMessage = new MqttApplicationMessage
                    {
                        Topic = clientId,
                        Payload = Encoding.Default.GetBytes("heartbeat"),
                        QualityOfServiceLevel = MqttQualityOfServiceLevel.AtLeastOnce,
                        Retain = false
                    };
                    await _mqttClient.PublishAsync(heartbeatMessage);
                    Console.WriteLine($"发送心跳包 at {DateTime.Now}");
                }
            }
            catch(Exception ex)
            {
                Console.WriteLine(ex.StackTrace);
            }
        }
        private void TryConnect(CancellationToken token)
        {
            Thread thread = new Thread(async () => {
                while (!token.IsCancellationRequested)
                {
                    Thread.Sleep(2*1000);
                    if (!_mqttClient.IsConnected)
                    {
                        try
                        {
                            //Console.WriteLine("尝试重新连接服务器...");
                            _form1.ShowLogMessage("尝试重新连接服务器...");
                            await _mqttClient.ReconnectAsync();
                        }
                        catch (Exception ex)
                        {
                            //Console.WriteLine($"重新连接服务器失败，异常信息：{ex.Message}");
                            _form1.ShowLogMessage($"重新连接服务器失败，异常信息：{ex.Message}");
                        }
                    }
                    else
                    {
                        //Console.WriteLine("send heart beat ping ########");
                        _form1.ShowLogMessage($"send heart beat ping {DateTime.Now}");
                        //await _mqttClient.TryPingAsync(); // 心跳检测
                        await ConnectAndPublishWithHeartBeat(mqttClientId);
                    }
                }       
            });
            thread.Start();
        }

        /// <summary>
        /// 客户端连接关闭事件
        /// </summary>
        /// <param name="arg"></param>
        /// <returns></returns>
        private Task _mqttClient_DisconnectedAsync(MqttClientDisconnectedEventArgs arg)
        {
            //Console.WriteLine($"客户端已断开与服务端的连接……");
            _form1.ShowLogMessage($"客户端已断开与服务端的连接……");
            _form1.ShowLogMessage($"断开原因：{arg.Exception}");
            return Task.CompletedTask;
        }

        /// <summary>
        /// 客户端连接成功事件
        /// </summary>
        /// <param name="arg"></param>
        /// <returns></returns>
        private Task _mqttClient_ConnectedAsync(MqttClientConnectedEventArgs arg)
        {
            //Console.WriteLine($"客户端已连接服务端……");
            _form1.ShowLogMessage($"客户端已连接服务端……");
            // 订阅消息主题
            // MqttQualityOfServiceLevel: （QoS）:  0 最多一次，接收者不确认收到消息，并且消息不被发送者存储和重新发送提供与底层 TCP 协议相同的保证。
            // 1: 保证一条消息至少有一次会传递给接收方。发送方存储消息，直到它从接收方收到确认收到消息的数据包。一条消息可以多次发送或传递。
            // 2: 保证每条消息仅由预期的收件人接收一次。级别2是最安全和最慢的服务质量级别，保证由发送方和接收方之间的至少两个请求/响应（四次握手）。
            _mqttClient.SubscribeAsync(subTopic, MqttQualityOfServiceLevel.AtLeastOnce);
            TryConnect(_cancellationToken);

            return Task.CompletedTask;
        }

        /// <summary>
        /// 收到消息事件
        /// </summary>
        /// <param name="arg"></param>
        /// <returns></returns>
        private Task _mqttClient_ApplicationMessageReceivedAsync(MqttApplicationMessageReceivedEventArgs arg)
        {
            //Console.WriteLine($"ApplicationMessageReceivedAsync：客户端ID=【{arg.ClientId}】接收到消息。 Topic主题=【{arg.ApplicationMessage.Topic}】 消息=【{Encoding.UTF8.GetString(arg.ApplicationMessage.Payload)}】 qos等级=【{arg.ApplicationMessage.QualityOfServiceLevel}】");
            //_form1.ShowLogMessage($"Received：客户端ID=【{arg.ClientId}】接收到消息。 Topic主题=【{arg.ApplicationMessage.Topic}】 消息=【{Encoding.UTF8.GetString(arg.ApplicationMessage.Payload)}】 qos等级=【{arg.ApplicationMessage.QualityOfServiceLevel}】");
            _form1.ShowSubMessage($"Received：客户端ID=【{arg.ClientId}】接收到消息。 Topic主题=【{arg.ApplicationMessage.Topic}】 消息=【{Encoding.UTF8.GetString(arg.ApplicationMessage.Payload)}】 qos等级=【{arg.ApplicationMessage.QualityOfServiceLevel}】");
            return Task.CompletedTask;
        }

        public async void Publish(string data)
        {
            var message = new MqttApplicationMessage
            {
                Topic = pubTopic,
                Payload = Encoding.Default.GetBytes(data),
                QualityOfServiceLevel = MqttQualityOfServiceLevel.AtLeastOnce,
                Retain = true  // 服务端是否保留消息。true为保留，如果有新的订阅者连接，就会立马收到该消息。
            };
            await _mqttClient.PublishAsync(message);
        }
    }
}
